﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Runtime.InteropServices;

namespace game
{
    public class AnimatedBillboard
    {
        public class Factory : FactoryPool<AnimatedBillboard>
        {
            public Factory(Scene scene, int initialSize, String textureFilename, int frameNum, int frameWidth, int frameHeight, int frameTimeMs, bool loop) 
                : base(scene, 0)
            {
                this.textureFilename = textureFilename;
                this.frameNum = frameNum;
                this.frameWidth = frameWidth;
                this.frameHeight = frameHeight;
                this.frameTimeMs = frameTimeMs;
                this.loop = loop;

                InitObjects(initialSize);
            }

            String textureFilename;
            int frameNum;
            int frameWidth;
            int frameHeight;
            int frameTimeMs;
            bool loop;

            public override AnimatedBillboard CreateObject()
            {
                AnimatedBillboard ep = new AnimatedBillboard(scene, textureFilename, frameNum, frameWidth, frameHeight, frameTimeMs, loop);
                return ep;
            }
        }

        protected Scene scene;

        public Vector3 Position
        { 
            get { return position; }
            set
            {
                position = value;
                verts[0].Position = position;
                verts[1].Position = position;
                verts[2].Position = position;
                verts[3].Position = position;
            }
        }
        protected Vector3 position;

        public bool IsAlive { get; protected set; }

        protected Texture texture;
        protected int frameNum;
        protected int currentFrame;
        protected int frameTimeMs;
        protected int timeAccumMs;
        protected Vector2[,] texCoords;
        protected bool looped;

        protected float frameProgress;

        protected static Effect effect;
        protected static EffectTechnique effectTech;
        private static EffectParameter ep_gWVP;
        private static EffectParameter ep_gView;
        private static EffectParameter ep_gTex;
        private static EffectParameter ep_gFrameProgress;

        protected AnimatedBillboardData[] verts = new AnimatedBillboardData[4];

        protected static short[] indices = new short[6] { 0, 1, 2, 0, 2, 3 };

        public AnimatedBillboard(Scene scene, String texFilename, int frameNum, int frameWidth, int frameHeight,
            int frameTimeMs, bool loop)
        {
            this.scene = scene;
            this.frameNum = frameNum;
            this.frameTimeMs = frameTimeMs;
            this.looped = loop;

            this.texture = ShooterGame.Instance.Content.Load<Texture>(texFilename);

            effect = ShooterGame.Instance.Content.Load<Effect>("Effects\\Billboard");
            effectTech = effect.Techniques["BillboardAnimated"];
            ep_gWVP = effect.Parameters["gWVP"];
            ep_gView = effect.Parameters["gView"];
            ep_gTex = effect.Parameters["gTex"];
            ep_gFrameProgress = effect.Parameters["gFrameProgress"];


            // ugly hack to retrieve size of the texture
            Texture2D tex2d = ShooterGame.Instance.Content.Load<Texture2D>(texFilename);
            int w = tex2d.Width;
            int h = tex2d.Height;
            // --

            if (w == 0 || h == 0)
            {
                throw new Exception("AnimTexture: width or height of texture " + texFilename + " equals zero.");
            }

            

            texCoords = new Vector2[frameNum, 4];

            int wOffset = 0, hOffset = frameHeight; // offset in pixels to the right-bottom corner of the current frame

            for (int i = 0; i < frameNum; i++)
            {
                wOffset += frameWidth;
                if (wOffset > w)
                {
                    wOffset -= w;
                    hOffset += frameHeight;
                    if (hOffset > h)
                    {
                        throw new Exception("AnimTexture: size of texture " + texFilename + " not large enough.");
                    }
                }

                texCoords[i, 0] = new Vector2((float)(wOffset - frameWidth) / (float)w, (float)(hOffset - frameHeight) / (float)h);
                texCoords[i, 1] = new Vector2((float)wOffset / (float)w, (float)(hOffset - frameHeight) / (float)h);
                texCoords[i, 2] = new Vector2((float)wOffset / (float)w, (float)hOffset / (float)h);
                texCoords[i, 3] = new Vector2((float)(wOffset - frameWidth) / (float)w, (float)hOffset / (float)h);


                Reset();
            
            }
        }

        public void SetSize(int width, int height)
        {
            verts[0].Offset = new Vector2(-width, -height);
            verts[1].Offset = new Vector2(width, -height);
            verts[2].Offset = new Vector2(width, height);
            verts[3].Offset = new Vector2(-width, height);
        }


        public void Reset()
        {
            currentFrame = 0;
            timeAccumMs = 0;
            frameProgress = 0.0f;
            IsAlive = true;

            verts[0].Position = Vector3.Zero;
            verts[1].Position = Vector3.Zero;
            verts[2].Position = Vector3.Zero;
            verts[3].Position = Vector3.Zero;
        }

        public void Update(GameTime gameTime)
        {
            if (!IsAlive)
                return;

            timeAccumMs += gameTime.ElapsedGameTime.Milliseconds;
            while (timeAccumMs > frameTimeMs)
            {
                timeAccumMs -= frameTimeMs;
                currentFrame++;
                if (!looped && currentFrame >= frameNum)
                {
                    IsAlive = false;
                }
                currentFrame %= frameNum;
            }

            frameProgress = (float)timeAccumMs / (float)frameTimeMs;

            verts[0].TexCoord1 = texCoords[currentFrame, 0];
            verts[1].TexCoord1 = texCoords[currentFrame, 1];
            verts[2].TexCoord1 = texCoords[currentFrame, 2];
            verts[3].TexCoord1 = texCoords[currentFrame, 3];

            int next = currentFrame < (frameNum - 1) ? currentFrame + 1 : currentFrame;
            verts[0].TexCoord2 = texCoords[next, 0];
            verts[1].TexCoord2 = texCoords[next, 1];
            verts[2].TexCoord2 = texCoords[next, 2];
            verts[3].TexCoord2 = texCoords[next, 3];
        }


        public void Draw(GameTime gameTime)
        {
            if (!IsAlive)
                return;

            ShooterGame.Instance.GraphicsDevice.VertexDeclaration
                = AnimatedBillboardData.VertexDecl;

            ep_gWVP.SetValue(scene.ActiveCamera.View * scene.ActiveCamera.Projection);
            ep_gView.SetValue(scene.ActiveCamera.View);
            ep_gTex.SetValue(texture);
            ep_gFrameProgress.SetValue(frameProgress);
            effect.CurrentTechnique = effectTech;

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                ShooterGame.Instance.GraphicsDevice.DrawUserIndexedPrimitives
                    <AnimatedBillboardData>(
                    PrimitiveType.TriangleList, verts, 0, 4, indices, 0, 2);
                pass.End();
            }
            effect.End();
        }

    }
}
